-module(wiki_controller).

-include("sew_app.hrl").

-export([
	catch_all/2, 
	before_call/2,
	do_redirect/2
]).

%%
%% all of our wiki-replated pages have an url like this 
%%
%% http://site/wiki/Page/
%% or
%% http://site/wiki/Page/action
%%
%% so we catch them all in catch_all
%%
%% TODO: Lowercase all incoming pagenames (Page and page should point to the same 
%%       page. As should Straße and STRASSE, by the way - should we run a node over
%%       jinterface to handle that with ICU?)
%%
%% TODO: HTTP 404 on page_not_found? Will be consistent with the overall RESTful
%%       look of the wiki
%%
%% TODO: Language versions. How to implement those? http://site/wiki/en:Page/ ?
%% 
%% TODO: Special pages. Allow introducing CouchDB views?
%%
%% TODO: Rework everything for ACL-based security. wiki-read, wiki-edit etc. 
%%       See also: http://kore-nordmann.de/blog/couchdb_a_use_case.html
%%
%% TODO: Resolve confilcts.
%%       Usecase 1: I've started creating a page. On submit it turned out 
%%                  that someone was quicker than me and created the page alredy
%%       Usecase 2: I've started editig a page. On submit it turned out 
%%                  that someone was quicker than me and created new revision
%%


%%
%% Guard against documents that mock design documents in CouchDB
%% Redirect any _page request to -page
%%
before_call(Any, [A, ["_"++Page]]) ->
	{do_redirect, [A, ["-" ++ Page]]};

before_call(Any, [A, ["_"++Page, Action]]) ->
	{do_redirect, [A, ["-" ++ Page, Action]]};

before_call(_Any, A) ->
	{catch_all, A}.

do_redirect(_A, [Page]) ->
	{ewr, wiki, Page};
do_redirect(_A, [Page, Action]) ->
	{ewr, wiki, Page, [Action]}.

catch_all(A, [Page, "create"]) ->
	%% do we already have such a page?
	D = couchdb:invoke_view("get", "page", [{"key", "\"" ++ Page ++ "\""}]),
	Doc = couchdb:single_record(D),
	case Doc of 
		{struct, L} ->
			%%
			%% we have a page, edit it
			%%
			{ewr, wiki, Page, ["edit"]};
		_ ->
			%%
			%% We've got new data. Create the page
			%%
			case yaws_arg:method(A) of
				'POST' ->
					Title = list_to_binary(form_utils:post(A, "title", "page title")),
					Data = list_to_binary(form_utils:post(A, "data", "")),
					Rendered = format:markdown(Data),
					Uri = list_to_binary(Page),
					NewDoc = [
						{<<"uri">>, Page},
						{<<"title">>, Title},
						{<<"data">>, Data},
						{<<"rendered">>, Rendered},
						{<<"type">>, <<"page">>}
					],
					couchdb:create_document({Page, NewDoc}),
					{ewr, wiki, Page};
				'GET' ->
					{data, {create, Page}}
			end
	end;

catch_all(A, [Page, "delete"]) ->
	%%
	%% TODO: get/page should suffice. Do we need to preview the page on delete?
	%%
	D = couchdb:invoke_view("get", "page-full", [{"key", "\"" ++ Page ++ "\""}]),
	Doc = couchdb:single_record(D),
	case Doc of 
		{struct, L} ->
			case yaws_arg:method(A) of
				'POST' ->
					ID = couchdb:get_id(Doc),
					Rev = couchdb:get_rev(Doc),
					couchdb:delete_document(ID, Rev),
					{ewr, wiki, Page};
				'GET' ->
					{data, {delete, Doc}}
			end;
		_ ->
			{data, {error, not_found, Page}}
	end;
	
	
catch_all(A, [Page, "edit"]) ->
	{json, {struct, Doc}} = couchdb:retrieve_document(Page),
	Error = proplists:get_value(<<"reason">>, Doc, <<"">>),
	case Error of 
		<<"">> ->
			case yaws_arg:method(A) of
				'POST' ->
					Title = form_utils:post(A, "title", "page title"),
					Data = form_utils:post(A, "data", ""),
					Rendered = format:markdown(Data),
					Rev = proplists:get_value(<<"_rev">>, Doc, <<"">>),
					%% don't forget to update every single field!
					%% TODO: Automate this somehow
					NewDoc = [
						{<<"uri">>, list_to_binary(Page)},
						{<<"title">>, list_to_binary(Title)},
						{<<"data">>, list_to_binary(Data)},
						{<<"rendered">>, Rendered},
						{<<"type">>, <<"page">>}
					],
					couchdb:update_document(Page, binary_to_list(Rev), NewDoc), 
					{ewr, wiki, Page};
				'GET' ->					
					{data, {edit, Doc}}
			end;
		_ ->
			{data, {error, not_found, Page}}
	end;
	
catch_all(A, [Page, "discuss"]) ->
	{data, {error, not_implemented, Page}};

catch_all(A, [Page, "history"]) ->
	D = couchdb:invoke_view("get", "page-full", [{"key", "\"" ++ Page ++ "\""}]),
	Doc = couchdb:single_record(D),
	case Doc of 
		{struct, L} ->
			ID = couchdb:get_id(Doc),
			Revs = couchdb:get_revs(ID),
			{data, {history, Doc, Revs}};
		_ ->
			{ewr, wiki, Page}
	end;

catch_all(A, ["index"]) ->
	{ewr, wiki, main};

catch_all(A, [Page]) ->
	{json, {struct, Doc}} = couchdb:retrieve_document(Page),
	Error = proplists:get_value(<<"reason">>, Doc, <<"">>),
	case Error of 
		<<"">> ->
			{data, {ok, Doc}};
		_ ->
			{data, {error, not_found, Page}}
	end;

%%
%% For all other usecases we pretend we've not implemented them yet
%%

catch_all(A, [Page, _]) ->
	{data, {error, not_implemented, Page}};
	
catch_all(A, [Page, _, _]) ->
	{data, {error, not_implemented, Page}};

catch_all(A, _) ->
	{ewr, wiki, main}.

